
local mt = {}
mt.__index = mt

mt.new = function(on_listener_added)
	return setmetatable({
		_listeners = {},
		_on_listener_added = on_listener_added
	}, mt)
end

mt.listen = function(this, listener, marker)
	if marker == nil then marker = true end
	this._listeners[listener] = marker
	if this._on_listener_added then this._on_listener_added(listener) end
end

mt.__call = function(this, listener, marker)
	return this:listen(listener, marker)
end

mt.unlisten = function(this, listener)
	this._listeners[listener] = nil
end

mt.unlisten_if = function(this, pred)
	for listener,marker in pairs(this._listeners) do
		if pred(marker) then this:unlisten(listener) end
	end
end

mt.clear = function(this)
	this._listeners = {}
end

mt.put = function(this, ...)
	if this._feedback then error("feedback detected") end
	this._feedback = true
	for listener in pairs(this._listeners) do
		listener(...)
	end
	this._feedback = nil
end

mt.put_if = function(this, pred, ...)
	if this._feedback then error("feedback detected") end
	this._feedback = true
	for listener,marker in pairs(this._listeners) do
		if pred(marker) then listener(...) end
	end
	this._feedback = nil
end

return mt

